#!/usr/bin/env python
#
# Copyright 2011 Justin Bruce Van Horne <justinvh@gmail.com>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#  http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

""" A tiling engine for adding, removing, and displaying tiles. """


import pyglet
import math


class TileEngine:
    _visible_tiles = []
    _visible_tiles_tot = 0
    _left, _right, _top, _bottom = 0, 0, 0, 0
    _cleanup = False
    enabled = True
    reset = True
    world = {}
    batch = pyglet.graphics.Batch()
    x, y = 0, 0
    tiles = 0

    def __init__(self, camera, tile_width=32, tile_height=32):
        self.tile_width = tile_width
        self.tile_height = tile_height
        self.camera = camera
        self.width, self.height = camera.width, camera.height
        self.x, self.y = camera.x, camera.y
        print 'World will be sliced into %d x %d pieces.' % (tile_width, tile_height)

    def add_tile(self, tile):
        w, h = int(self.tile_width), int(self.tile_height)
        x, y = int(tile.x), int(tile.y)
        ww, wh = tile.tile_width, tile.tile_height

        # Create our bounding box
        left = x / w
        right = left + (ww / w)
        bottom = y / h
        top = bottom + (wh / h)

        if left == right:
            if top == bottom:
                i, j = left, top
                #print 'Tile is located at one location (%d, %d)' % (i, j)
                if i in self.world and j in self.world[i]:
                    self.world[i][j].append(tile)
                elif i in self.world:
                    self.world[i][j] = [tile]
                else:
                    self.world[i] = { j: [tile] }
            else:
                i = left
                #print 'Tile is located in a column (%d, (%d to %d))' % (i, bottom, top)
                for j in range(bottom, top):
                    if i in self.world and j in self.world[i]:
                        self.world[i][j].append(tile)
                    elif i in self.world:
                        self.world[i][j] = [tile]
                    else:
                        self.world[i] = { j: [tile] }
        elif top == bottom:
            j = top 
            #print 'Tile is located in a row ((%d to %d), %d)' % (left, right, j)
            for i in range(left, right):
                if i in self.world and j in self.world[i]:
                    self.world[i][j].append(tile)
                elif i in self.world:
                    self.world[i][j] = [tile]
                else:
                    self.world[i] = { j: [tile] }
        else:
            #print 'Tile is being added from %d to %d and from %d to %d' % (left, right, bottom, top)
            for i in range(left, right + 1):
                for j in range(bottom, top + 1):
                    if i in self.world and j in self.world[i]:
                        self.world[i][j].append(tile)
                    elif i in self.world:
                        self.world[i][j] = [tile]
                    else:
                        self.world[i] = { j: [tile] }

        tile.visible = False
        self.tiles += 1

    def cleanup(self):
        self._cleanup = True

        print 'Cleaning up. Removing %d tiles.' % self._visible_tiles_tot

        # Cleanup the batch
        for tile in self._visible_tiles:
            tile.batch = None
        self._visible_tiles = []
        self._visible_tiles_tot = 0
        self._left, self._right, self._top, self._bottom = 0, 0, 0, 0

        batch = self.generate_batch()

        # Cleanup disabled
        self._cleanup = False
        return batch


    def generate_batch(self):
        if self.camera.x == self.x and self.camera.y == self.y: 
            return self.batch

        if not self.enabled:
            self.reset = True
            return self.batch

        self.x, self.y = -self.camera.x, self.camera.y
        w, h = int(self.tile_width), int(self.tile_height)
        x, y = int(self.x), int(self.y)
        ww, wh = self.width, self.height

        # Create our bounding box
        left = x / w
        right = left + (ww / w)
        bottom = y / h
        top = bottom + (wh / h)

        # If we're still in the previous slice, then don't do anything
        if self._left == left and self._right == right and self._top == top and self._bottom == bottom:
            return self.batch

        """
        tiles_removed = 0
        # Remove the tiles from the left side
        for i in range(left - 2, left - 1):
            for j in range(bottom - 1, top + 1):
                if i in self.world and j in self.world[i]:
                    for tile in self.world[i][j]:
                        tile.batch = None
                        tiles_removed += 1

        # Remove the tiles from the right side
        for i in range(right + 1, right + 2):
            for j in range(bottom - 1, top + 1):
                if i in self.world and j in self.world[i]:
                    for tile in self.world[i][j]:
                        tile.batch = None
                        tiles_removed += 1

        # Remove the tiles from the top
        for i in range(left - 1, right + 1):
            for j in range(top + 1, top + 2):
                if i in self.world and j in self.world[i]:
                    for tile in self.world[i][j]:
                        tile.batch = None
                        tiles_removed += 1

        # Remove the tiles from the bottom
        for i in range(left - 1, right + 1):
            for j in range(bottom - 2, bottom - 1):
                if i in self.world and j in self.world[i]:
                    for tile in self.world[i][j]:
                        tile.batch = None
                        tiles_removed += 1


        # Remove the tiles from the right side
        for i in range(right + 1, right + 2):
            for j in range(bottom - 2, bottom - 1):
                if i in self.world and j in self.world[i]:
                    for tile in self.world[i][j]:
                        tile.batch = None
                        tiles_removed += 1
        """

        if not self.reset:
            # Figure out our deltas.
            if left < self._left:
                # We've moved to the left
                dl = self._left - left
                right = dl
            elif right > self._right:
                # We've moved to the right
                dr = right - self._right
                left = dr

            if bottom < self._bottom:
                # We've moved down
                db = self._bottom - bottom
                top = db
            elif top > self._top:
                # We've moved up
                dt = top - self._top
                bottom = dt
        else:
            self.reset = False

        # Retrieve tiles including tiles around the edges
        tiles_added = 0
        for i in range(left - 1, right + 1):
            for j in range(bottom - 1, top + 1):
                if i in self.world and j in self.world[i]:
                    for tile in self.world[i][j]:
                        if tile.batch != self.batch:
                            self._visible_tiles_tot += 1
                            self._visible_tiles.append(tile)
                            tile.batch = self.batch
                            tile.visible = True
                            tiles_added += 1

        self._left, self._right, self._bottom, self._top = left, right, bottom, top

        return self.batch

    def toggle(self):
        self.enabled = not self.enabled
        print 'Enable: %s' % self.enabled
